/**
 * 简单物理引擎
 * Created by gzj on 16/10/12.
 */

class Color {
    constructor(r, g, b) {
        const formater = (c) => Math.abs(parseInt(c));
        this.r = formater(r);
        this.g = formater(g);
        this.b = formater(b);
    }

    get style() {
        return `rgb(${this.r},${this.g},${this.b})`;
    }
}


/**
 * 物理实体
 */
class PhyObj {
    /**
     * @param x {number} 实体左下角在X轴的位置, m
     * @param y {number} 实体左下角在Y轴的位置, m
     * @param width {number} 实体宽度
     * @param height {number} 实体高度
     * @param vx {number} 实体在x轴的速度, m/s
     * @param vy {number} 实体在y轴的速度, m/s
     * @param m {number} 实体的质量, kg
     */
    constructor(x, y, width, height, vx=0, vy=0, m=1) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        this.vx = vx;
        this.vy = vy;
        this.m = Math.abs(m);

        const randomC = () => parseInt(Math.random() * 255);
        this.color = new Color(randomC(), randomC(), randomC());
    }

    // 最左边界
    get minX() {return this.x;}

    // 最右边界
    get maxX() {return this.x + this.width;}

    // 最下边界
    get minY() {return this.y;}

    // 最上边界
    get maxY() {return this.y + this.height;}
}


/**
 * 自己实现的简单物理引擎
 */
class MyWorld {
    /**
     * @param width {number} 世界的宽度， m
     * @param height {number} 世界的高度， m
     * @param timeScale {number} 时间流逝速度缩放倍数
     * @param gravity {number} 重力加速度，m/s^2
     * @param airFriction {number} 空气摩擦力, N
     */
    constructor(width=300, height=300, timeScale=1, gravity=9.8, airFriction=1) {
        this.width = width;
        this.height = height;
        this.timeScale = timeScale;
        this.gravity=Math.abs(gravity) * -1;
        this.airFriction = Math.abs(airFriction);

        this.lastCalTime = 0;
        this.objs = {};
    }

    /**
     * 向世界中加入物理实体
     * @param obj {PhyObj}
     * @return {number} 物理实体在世界中的编号
     */
    addObj(obj) {
        const id = now();
        this.objs[id] = obj;
        return id;
    }

    /**
     * 移除世界中的物理实体
     * @param id {number} 物理实体在世界中的编号
     */
    removeObj(id) {
        delete this.objs[id];
    }

    /**
     * 获取世界中的物理实体
     * @param id {number} 物理实体在世界中的编号
     * @return {PhyObj}
     */
    getObj(id) {
        return this.objs[id];
    }

    /**
     * 计算世界中所有实体的物理信息
     */
    calculate() {
        const nowTime = now();
        if(!this.lastCalTime) {
            this.lastCalTime = nowTime;
        }

        const timeDelta = (nowTime - this.lastCalTime) / 1000 * this.timeScale;
        this.lastCalTime = nowTime;

        for(let id in this.objs) {
            if(this.objs.hasOwnProperty(id)) {
                let obj = this.objs[id];
                // 阻力加速度
                const fa = this.airFriction / obj.m;

                // 垂直速度方向
                const yvd = obj.vy > 0 ? 1 : -1;
                // 垂直阻力方向
                const yfd = - yvd;

                // 综合垂直加速度
                const a = (this.gravity + yfd * fa);

                obj.y = obj.y < 0 ? 0 : obj.y;

                // y轴方向位置
                obj.y += obj.vy * timeDelta + a * Math.pow(timeDelta, 2) / 2;

                // y轴方向速度
                obj.vy += a * timeDelta;

                obj.vy = obj.y <= 0? Math.abs(obj.vy) : obj.vy;


                // 水平速度方向
                const xvd = obj.vx > 0 ? 1 : -1;
                // 水平阻力方向
                const xfd = - xvd;

                // x轴方向位置
                obj.x += obj.vx * timeDelta + xfd * fa * Math.pow(timeDelta, 2) / 2;

                // x轴方向的速度
                if(obj.vx) {
                    const oldV = obj.vx;
                    obj.vx += xfd * fa * timeDelta;

                    if(getDirection(oldV) !== getDirection(obj.vx)) {
                        obj.vx = 0;
                    }
                }

                if(obj.maxX <= 0 || obj.minX >= this.width || obj.maxY < 0) {
                    this.removeObj(id);
                    console.log(`移除实体[${id}] =>`, obj);
                }
            }
        }
    }
}


/**
 * 使用PhysicsJS封装的物理引擎
 */
class PhyWorld {
    /**
     * @param width {number} 世界的宽度， m
     * @param height {number} 世界的高度， m
     * @param timeScale {number} 时间流逝速度缩放倍数
     * @param gravity {number} 重力加速度，m/s^2
     * @param airFriction {number} 空气摩擦力, N
     */
    constructor(width=300, height=300, timeScale=1, gravity=9.8, airFriction=1) {
        this.world = Physics();

        this.world.add(Physics.behavior('constant-acceleration', {
            acc: { x : 0, y: 9.8 } // this is the default
        }));

        this.bodys = {};
    }

    get objs() {return this.world.getBodies()}

    /**
     * 向世界中加入物理实体
     * @param obj {PhyObj}
     * @return {number} 物理实体在世界中的编号
     */
    addObj(obj) {
        const id = now();
        this.bodys[id] = Physics.body('circle', {
            x: obj.x,
            y: obj.y,
            vx: obj.vx,
            vy: obj.vy,
            radius: obj.width
        });
        this.bodys[id].color = obj.color;

        this.world.add(this.bodys[id]);
        return id;
    }

    /**
     * 移除世界中的物理实体
     * @param id {number} 物理实体在世界中的编号
     */
    removeObj(id) {
        const body = this.bodys[id];
        if(body) {
            delete this.bodys[id];
            this.world.removeBody(body);
        }
    }

    /**
     * 获取世界中的物理实体
     * @param id {number} 物理实体在世界中的编号
     * @return {PhyObj}
     */
    getObj(id) {
        const body = this.bodys[id];
        return new PhyObj(
            body.state.pos.x,
            body.state.pos.y,
            body.radius,
            body.radius,
            body.state.vel.x,
            body.state.vel.y
        );
    }

    /**
     * 计算世界中所有实体的物理信息
     */
    calculate() {
        this.world.step(now());
    }
}